<?php
/*=============================================================================
|| ##################################################################
||	phpFoX Konsort
|| ##################################################################
||
||	Copyright		: (C) 2005-2007 PHPFOX Limited a Reality Fox Creation
||	Contact			: info@phpfox.com
||
||	- phpFoX and all of its source code and files are protected by Copyright Laws.
||
||	- The license for phpFoX permits you to install this software on a single domain (.com, .org, .net, etc.).
||
||	- You may also not remove this copyright screen which shows the copyright information and credits for phpFoX (RealityFOX Creations).
||
||	- phpFoX Konsort is NOT a FREE software - http://www.phpfox.com/license/
||
|| ##################################################################
=============================================================================*/

/* $File: ./include/classes/mail/mime.class.php, $Author:  $ */

require_once 'mimepart.class.php';

class Mail_Mime
{
    /**
     * The html part of the message
     * @var string
     */
    var $html;

    /**
     * The text part of the message(only used in TEXT only messages)
     * @var string
     */
    var $text;

    /**
     * The main body of the message after building
     * @var string
     */
    var $output;

    /**
     * The alternative text to the HTML part (only used in HTML messages)
     * @var string
     */
    var $html_text;

    /**
     * An array of embedded images/objects
     * @var array
     */
    var $html_images;

    /**
     * An array of recognised image types for the findHtmlImages() method
     * @var array
     */
    var $image_types;

    /**
     * Default image dir for function setHtml
     * @var string
     */
    var $images_dir;

    /**
     * Parameters that affect the build process
     * @var array
     */
    var $build_params;

    /**
     * Array of attachments
     * @var array
     */
    var $attachments;

    /**
     * The main message headers
     * @var array
     */
    var $headers;

    /**
     * Whether the message has been built or not
     * @var boolean
     */
    var $is_built;

    /**
     * The return path address.
     * @var string
     */
    var $return_path;

    /**
     * Array of information needed for smtp sending
     * @var array
     */
    var $smtp_params;

    /**
     * Constructor function. Sets the headers
     * if supplied.
     */
    function Mail_Mime()
    {
        // Initialise fields
        $this->html_images = array();
        $this->headers     = array();
        $this->is_built    = false;

        // If you want the auto load functionality
        // to find other image/file types, add the
        //  extension and content type here.
        $this->image_types = array(
            'gif'  => 'image/gif',
            'jpg'  => 'image/jpeg',
            'jpeg' => 'image/jpeg',
            'jpe'  => 'image/jpeg',
            'bmp'  => 'image/bmp',
            'png'  => 'image/png',
            'tif'  => 'image/tiff',
            'tiff' => 'image/tiff',
            'swf'  => 'application/x-shockwave-flash'
        );

        //set build params
        $this->build_params['html_encoding'] = 'quoted-printable';
        $this->build_params['text_encoding'] = '7bit';
        $this->build_params['html_charset']  = 'UTF-8';
        $this->build_params['text_charset']  = 'UTF-8';
        $this->build_params['head_charset']  = 'UTF-8';
        $this->build_params['text_wrap']     = 998;

        // Defaults smtp sending
        if (!empty($GLOBALS['HTTP_SERVER_VARS']['HTTP_HOST'])) {
            $helo = $GLOBALS['HTTP_SERVER_VARS']['HTTP_HOST'];
        } elseif (!empty($GLOBALS['HTTP_SERVER_VARS']['SERVER_NAME'])) {
            $helo = $GLOBALS['HTTP_SERVER_VARS']['SERVER_NAME'];
        } else {
            $helo = 'localhost';
        }

        $this->smtp_params['host'] = 'localhost';
        $this->smtp_params['port'] = 25;
        $this->smtp_params['helo'] = $helo;
        $this->smtp_params['auth'] = false;
        $this->smtp_params['user'] = '';
        $this->smtp_params['pass'] = '';

        // Make sure the MIME version header is first.
        $this->headers['MIME-Version'] = '1.0';
    }


    /** Set the CRLF style
     * @param string $crlf new CRLF
     */
    function setCrlf($crlf = "\n")
    {
        if (!defined('CRLF')) {
            define('CRLF', $crlf, true);
        }

        if (!defined('MAIL_MIMEPART_CRLF')) {
            define('MAIL_MIMEPART_CRLF', $crlf, true);
        }
    }

    /**Set the SMTP parameters
     */
    function setSMTPParams($host = null, $port = null, $helo = null, $auth = null, $user = null, $pass = null)
    {
        if (!is_null($host)) $this->smtp_params['host'] = $host;
        if (!is_null($port)) $this->smtp_params['port'] = $port;
        if (!is_null($helo)) $this->smtp_params['helo'] = $helo;
        if (!is_null($auth)) $this->smtp_params['auth'] = $auth;
        if (!is_null($user)) $this->smtp_params['user'] = $user;
        if (!is_null($pass)) $this->smtp_params['pass'] = $pass;
    }

    /** Set the text encoding
     * @param string $encoding text encoding (7biy, quote-printable etc.)
     */
    function setTextEncoding($encoding = '7bit')
    {
        $this->build_params['text_encoding'] = $encoding;
    }

    /** Set the HTML encoding
     * @param string $encoding text encoding (7biy, quote-printable etc.)
     */
    function setHtmlEncoding($encoding = 'quoted-printable')
    {
        $this->build_params['html_encoding'] = $encoding;
    }

    /** Set the text charset
     * @param string $charset charset for text (ISO-8859-1 by default)
     */
    function setTextCharset($charset = 'UTF-8')
    {
        $this->build_params['text_charset'] = $charset;
    }

    /** Set the HTML charset
     * @param string $charset charset for HTML (ISO-8859-1 by default)
     */
    function setHtmlCharset($charset = 'UTF-8')
    {
        $this->build_params['html_charset'] = $charset;
    }

    /** Set the header encoding charset
     * @param string $charset charset for HTML (ISO-8859-1 by default)
     */
    function setHeadCharset($charset = 'UTF-8')
    {
        $this->build_params['head_charset'] = $charset;
    }

    /** Set the text wrap count
     * @param int $count number of symbols to wrap
     */
    function setTextWrap($count = 998)
    {
        $this->build_params['text_wrap'] = $count;
    }

    /** Set a header
     * @param string $name  header name
     * @param string $value header value
     */
    function setHeader($name, $value)
    {
        $this->headers[$name] = $value;
    }

    /** Set `Subject`
     * @param string $subject subject
     */
    function setSubject($subject)
    {
        $this->headers['Subject'] = $subject;
    }

    /** Set `From`
     * @param string $from from value
     */
    function setFrom($from)
    {
        $this->headers['From'] = $from;
    }

    /** Set `return-path`
     * @param string $return_path return path
     */
    function setReturnPath($return_path)
    {
        $this->return_path = $return_path;
    }

    /** Set `Cc`
     * @param string $cc CC
     */
    function setCc($cc)
    {
        $this->headers['Cc'] = $cc;
    }

    /** Set `Bcc`
     * @param string $bcc BCC
     */
    function setBcc($bcc)
    {
        $this->headers['Bcc'] = $bcc;
    }

    /** Adds plain text. Use this function
     * when sending non-html email (text-only)
     * @param string $text message text
     * @see setHtml()
     */
    function setText($text = '')
    {
        $this->text = $text;
    }

    /** Adds a html part to the mail.
     * Also replaces image names with content-id's.
     * @param string $html       html code of message
     * @param string $text       text variant of message
     * @param string $images_dir directory with images
     */
    function setHtml($html, $text = null, $images_dir = null)
    {
        $this->html      = $html;
        $this->html_text = $text;
        $images_dir = $images_dir ? $images_dir: $this->images_dir;

        if (isset($images_dir)) {
            $this->_findHtmlImages($images_dir);
        }
    }

    /** Function for extracting images from html source.
     * This function will look through the html code supplied
     * by add_html() and find inside html-tags any file that
     * ends in one of the extensions defined in $obj->image_types.
     * If the file exists it will read it in and
     * embed it, (not an attachment).
     * @param string $images_dir directory with images
     */
    function _findHtmlImages($images_dir)
    {
        static $extensions = array();
    	
    	// Build the list of image extensions
        while (list($key,) = each($this->image_types)) {
            $extensions[] = $key;
        }

        preg_match_all('/(?:\<[^\>]+src=\"|\')([^\"\']+\.('.implode('|', $extensions).'))(?:\"|\'[^\>]?\>)/Ui', $this->html, $images);

        for ($i=0; $i<count($images[1]); $i++) {
            if (file_exists($images_dir . $images[1][$i])) {
                $html_images[] = $images[1][$i];
                $this->html = str_replace($images[1][$i], basename($images[1][$i]), $this->html);
            }
        }

        if (!empty($html_images)) {

            // If duplicate images are embedded, they may show up as attachments, so remove them.
            $html_images = array_unique($html_images);
            sort($html_images);

            for ($i=0; $i<count($html_images); $i++) {
                if ($image = file_get_contents($images_dir.$html_images[$i])) {
                    $ext = substr($html_images[$i], strrpos($html_images[$i], '.') + 1);
                    $content_type = $this->image_types[strtolower($ext)];
                    $this->addHtmlImage($image, basename($html_images[$i]), $content_type);
                }
            }
        }
    }

    /** Adds an image to the list of embedded images.
     * @param unknown_type $file
     * @param unknown_type $name
     * @param unknown_type $c_type
     */
    function addHtmlImage($file, $name = '', $c_type='application/octet-stream')
    {
        $this->html_images[] = array(
            'body'   => $file,
            'name'   => $name,
            'c_type' => $c_type,
            'cid'    => md5(uniqid(time()))
        );
    }


    /** Adds a file to the list of attachments.
     * @param unknown_type $file
     * @param unknown_type $name
     * @param unknown_type $c_type
     * @param unknown_type $encoding
     */
    function addAttachment($file, $name = '', $c_type='application/octet-stream', $encoding = 'base64')
    {
        $this->attachments[] = array(
            'body'     => $file,
            'name'     => $name,
            'c_type'   => $c_type,
            'encoding' => $encoding
        );
    }

    /** Adds a text subpart to a mime_part object
     * @param unknown_type $obj
     * @param unknown_type $text
     * @return unknown
     */
    function &_addTextPart(&$obj, $text)
    {
        $params['content_type'] = 'text/plain';
        $params['encoding']     = $this->build_params['text_encoding'];
        $params['charset']      = $this->build_params['text_charset'];
        if (is_object($obj))
        $oPart = &$obj->addSubpart($text, $params);
        else
        $oPart = &new Mail_mimePart($text, $params);

        return $oPart;
    }

    /** Adds a html subpart to a mime_part object
     * @param unknown_type $obj
     * @return unknown
     */
    function &_addHtmlPart(&$obj)
    {
        $params['content_type'] = 'text/html';
        $params['encoding']     = $this->build_params['html_encoding'];
        $params['charset']      = $this->build_params['html_charset'];
        if (is_object($obj)) {
            $oPart = &$obj->addSubpart($this->html, $params);
        } else {
            $oPart = &new Mail_mimePart($this->html, $params);
        }
        return $oPart;
    }

    /**
     * Starts a message with a mixed part
     * @return unknown
     */
    function &_addMixedPart()
    {
        $params['content_type'] = 'multipart/mixed';
        $oPart = &new Mail_mimePart('', $params);
        return $oPart;
    }

    /** Adds an alternative part to a mime_part object
     * @param unknown_type $obj
     * @return unknown
     */
    function &_addAlternativePart(&$obj)
    {
        $params['content_type'] = 'multipart/alternative';
        if (is_object($obj))
            $oPart = &$obj->addSubpart('', $params);
        else
            $oPart = &new Mail_mimePart('', $params);

        return $oPart;
    }

    /** Adds a html subpart to a mime_part object
     * @param unknown_type $obj
     * @return unknown
     */
    function &_addRelatedPart(&$obj)
    {
        $params['content_type'] = 'multipart/related';
        if (is_object($obj)) {
            $oPart = &$obj->addSubpart('', $params);
        } else {
            $oPart = &new Mail_mimePart('', $params);
        }
        return $oPart;
    }

    /** Adds an html image subpart to a mime_part object
     * @param unknown_type $obj
     * @param unknown_type $value
     */
    function _addHtmlImagePart(&$obj, $value)
    {
        $params['content_type'] = $value['c_type'];
        $params['encoding']     = 'base64';
        $params['disposition']  = 'inline';
        $params['dfilename']    = $value['name'];
        $params['cid']          = $value['cid'];
        $obj->addSubpart($value['body'], $params);
    }

    /** Adds an attachment subpart to a mime_part object
     * @param unknown_type $obj
     * @param unknown_type $value
     */
    function _addAttachmentPart(&$obj, $value)
    {
        $params['content_type'] = $value['c_type'];
        $params['encoding']     = $value['encoding'];
        $params['disposition']  = 'attachment';
        $params['dfilename']    = $value['name'];
        $obj->addSubpart($value['body'], $params);
    }

    /** Builds the multipart message from the
     * list ($this->_parts). $params is an
     * array of parameters that shape the building
     * of the message. Currently supported are:
     *   html_encoding - The type of encoding to use on html. Valid options are
     *                   "7bit", "quoted-printable" or "base64" (all without quotes).
     *                   7bit is EXPRESSLY NOT RECOMMENDED. Default is quoted-printable
     *   text_encoding - The type of encoding to use on plain text Valid options are
     *                   "7bit", "quoted-printable" or "base64" (all without quotes).
     *                   Default is 7bit
     *   text_wrap     - The character count at which to wrap 7bit encoded data.
     *                   Default this is 998.
     *   html_charset  - The character set to use for a html section.
     *                   Default is ISO-8859-1
     *   text_charset  - The character set to use for a text section.
     *                   Default is ISO-8859-1
     *   head_charset  - The character set to use for header encoding should it be needed.
     *                   Default is ISO-8859-1
     */
    function buildMessage($params = array())
    {
        if (!empty($params)) {
            while (list($key, $value) = each($params)) {
                $this->build_params[$key] = $value;
            }
        }

        if (!empty($this->html_images)) {
            foreach ($this->html_images as $value) {
                $this->html = str_replace($value['name'], 'cid:'.$value['cid'], $this->html);
            }
        }

        $null        = null;
        $attachments = !empty($this->attachments) ? true : false;
        $html_images = !empty($this->html_images) ? true : false;
        $html        = !empty($this->html)        ? true : false;
        $text        = isset($this->text)         ? true : false;

        switch (true) {

            case $text AND !$attachments:
                $message = &$this->_addTextPart($null, $this->text);
                break;

            case !$text AND $attachments AND !$html:
                $message = &$this->_addMixedPart();

                for ($i=0; $i<count($this->attachments); $i++) {
                    $this->_addAttachmentPart($message, $this->attachments[$i]);
                }
                break;

            case $text AND $attachments:
                $message = &$this->_addMixedPart();
                $this->_addTextPart($message, $this->text);

                for ($i=0; $i<count($this->attachments); $i++) {
                    $this->_addAttachmentPart($message, $this->attachments[$i]);
                }
                break;

            case $html AND !$attachments AND !$html_images:
                if (!is_null($this->html_text)) {
                    $message = &$this->_addAlternativePart($null);
                    $this->_addTextPart($message, $this->html_text);
                    $this->_addHtmlPart($message);
                } else {
                    $message = &$this->_addHtmlPart($null);
                }
                break;


            case $html AND !$attachments AND $html_images:
                if (!is_null($this->html_text)) {
                    $message = &$this->_addAlternativePart($null);
                    $this->_addTextPart($message, $this->html_text);
                    $related = &$this->_addRelatedPart($message);
                } else {
                    $message = &$this->_addRelatedPart($null);
                    $related = &$message;
                }
                $this->_addHtmlPart($related);
                for ($i=0; $i<count($this->html_images); $i++) {
                    $this->_addHtmlImagePart($related, $this->html_images[$i]);
                }
                break;

            case $html AND $attachments AND !$html_images:
                $message = &$this->_addMixedPart();
                if (!is_null($this->html_text)) {
                    $alt = &$this->_addAlternativePart($message);
                    $this->_addTextPart($alt, $this->html_text);
                    $this->_addHtmlPart($alt);
                } else {
                    $this->_addHtmlPart($message);
                }
                for ($i=0; $i<count($this->attachments); $i++) {
                    $this->_addAttachmentPart($message, $this->attachments[$i]);
                }
                break;

            case $html AND $attachments AND $html_images:
                $message = &$this->_addMixedPart();
                if (!is_null($this->html_text)) {
                    $alt = &$this->_addAlternativePart($message);
                    $this->_addTextPart($alt, $this->html_text);
                    $rel = &$this->_addRelatedPart($alt);
                } else {
                    $rel = &$this->_addRelatedPart($message);
                }
                $this->_addHtmlPart($rel);
                for ($i=0; $i<count($this->html_images); $i++) {
                    $this->_addHtmlImagePart($rel, $this->html_images[$i]);
                }
                for ($i=0; $i<count($this->attachments); $i++) {
                    $this->_addAttachmentPart($message, $this->attachments[$i]);
                }
                break;

        }

        if (isset($message)) {
            $output = $message->encode();
            $this->output   = $output['body'];
            $this->headers  = array_merge($this->headers, $output['headers']);

            // Add message ID header
            srand((double)microtime()*10000000);
            $sReplaceWith = "";
  			if ( !empty($GLOBALS['HTTP_SERVER_VARS']['HTTP_HOST']) )
			{
				$sReplaceWith = $GLOBALS['HTTP_SERVER_VARS']['HTTP_HOST'];
			}
			else
			{
				if ( !empty($GLOBALS['HTTP_SERVER_VARS']['SERVER_NAME']) )
				{
					$sReplaceWith = $GLOBALS['HTTP_SERVER_VARS']['SERVER_NAME'];
				}
			}
			$message_id = sprintf('<%s.%s@%s>', base_convert(time(), 10, 36), base_convert(rand(), 10, 36),$sReplaceWith);
            $this->headers['Message-ID'] = $message_id;

            //$this->is_built = true;
            return true;
        } else {
            return false;
        }
    }

    /** Function to encode a header if necessary
     * according to RFC2047
     */
    function _encodeHeader($input, $charset = 'UTF-8')
    {
    	preg_match_all('/([\w\-]*[\x80-\xFF]+[\w\-]*(\s+[\w\-]*[\x80-\xFF]+[\w\-]*)*)\s*/', $input, $matches);
        foreach ($matches[1] as $value) {
            $replacement = preg_replace('/([\s_=\?\x80-\xFF])/e', '"=" . strtoupper(dechex(ord("\1")))', $value);
            $input = str_replace($value, '=?' . $charset . '?Q?' . $replacement . '?=', $input);
        }
        return $input;
    }

	function cleanHeaders($sStr)
	{
		$aReplace = array("%0a", "%0d", "Content-Type:", "bcc:","to:","cc:");

		foreach($aReplace as $sReplace)
		{
			$sStr = preg_replace("/". $sReplace ."/i", "", $sStr);
		}

		return $sStr;
	}

    /** Sends the mail.
     * @param  array  $recipients
     * @param  string $type OPTIONAL
     * @return mixed
     */
    function send($recipients, $type = 'mail')
    {
        if (!defined('CRLF')) {
            $this->setCrlf($type == 'mail' ? "\n" : "\r\n");
        }

        if (!$this->is_built) {
            $this->buildMessage();
        }

        $type = App::getParam('mail_type');

		$this->headers = array_map(array('Mail_Mime', 'cleanHeaders'), $this->headers);

        switch ($type) {
            case 'mail':
                $subject = '';
                if (!empty($this->headers['Subject'])) {
                    $subject = $this->_encodeHeader($this->headers['Subject'], $this->build_params['head_charset']);
                    unset($this->headers['Subject']);
                }

                // Get flat representation of headers
                foreach ($this->headers as $name => $value) {
                    $headers[] = $name . ': ' . $this->_encodeHeader($value, $this->build_params['head_charset']);
                }

                $to = $this->_encodeHeader(implode(', ', $recipients), $this->build_params['head_charset']);

                if (!empty($this->return_path)) {
                    $result = mail($to, $subject, $this->output, implode(CRLF, $headers), '-f' . $this->return_path);
                } else {
                    $result = mail($to, $subject, $this->output, implode(CRLF, $headers));
                }

                // Reset the subject in case mail is resent
                if ($subject !== '') {
                    $this->headers['Subject'] = $subject;
                }

                // Return
                return $result;
                break;

            case 'smtp':
                require_once App::getParam('path') . 'include/classes/mail/smtp.class.php';

                $smtp = &smtp::connect($this->smtp_params);

                // Parse recipients argument for internet addresses
                $smtp_recipients = $recipients;

                // Get flat representation of headers, parsing
                // Cc and Bcc as we go
                foreach ($this->headers as $name => $value) {
                    if ($name == 'Cc' OR $name == 'Bcc') {
                        $smtp_recipients[] = $value;
                    }
                    if ($name == 'Bcc') {
                        continue;
                    }
                    $headers[] = $name . ': ' . $this->_encodeHeader($value, $this->build_params['head_charset']);
                }
                // Add To header based on $recipients argument
                $headers[] = 'To: ' . $this->_encodeHeader(implode(', ', $recipients), $this->build_params['head_charset']);

                // Add headers to send_params
                $send_params['headers']    = $headers;
                $send_params['recipients'] = array_values(array_unique($smtp_recipients));
                $send_params['body']       = $this->output;

                // Setup return path
                if (isset($this->return_path)) {
                    $send_params['from'] = $this->return_path;
                } else {
                    $send_params['from'] = 'postmaster@' . $this->smtp_params['helo'];
                }
                // Send it
                if (!$smtp->send($send_params)) {
                    $this->errors = $smtp->errors;
                    return false;
                }
                return true;
                break;
        }
    }

    /**
     * Use this method to return the email
     * in message/rfc822 format. Useful for
     * adding an email to another email as
     * an attachment. there's a commented
     * out example in example.php.
     */
/*    function getRFC822($recipients)
    {
    // Make up the date header as according to RFC822
    $this->setHeader('Date', date('D, d M y H:i:s O'));

    if (!defined('CRLF')) {
    $this->setCrlf($type == 'mail' ? "\n" : "\r\n");
    }

    if (!$this->is_built) {
    $this->buildMessage();
    }

    // Return path ?
    if (isset($this->return_path)) {
    $headers[] = 'Return-Path: ' . $this->return_path;
    }

    // Get flat representation of headers
    foreach ($this->headers as $name => $value) {
    $headers[] = $name . ': ' . $value;
    }
    $headers[] = 'To: ' . implode(', ', $recipients);

    return implode(CRLF, $headers) . CRLF . CRLF . $this->output;
    }*/


} // End of class.
?>